package simulations;

import forces.Force;
import masses.Mass;
import springs.AbstractSpring;

import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * Represents a model from a .xsp file
 * 
 * @author Zach Michaelov
 *         edited by Jenni Mercado
 */
public class Entity {
    /**
     * List of masses in the model
     */
    private List<Mass> myMasses;
    /**
     * List of springs in the model
     */
    private List<AbstractSpring> mySprings;

    /**
     * Create a Canvas with the given size.
     */
    public Entity () {
        myMasses = new ArrayList<Mass>();
        mySprings = new ArrayList<AbstractSpring>();
    }

    /**
     * add a Mass to the simulation
     * 
     * @param mass
     */
    public void add (Mass mass) {
        myMasses.add(mass);
    }

    /**
     * Add a spring to the simulation
     * 
     * @param spring
     */
    public void add (AbstractSpring spring) {
        mySprings.add(spring);
    }

    /**
     * Paint all shapes on the canvas.
     * 
     * @param pen used to paint shape on the screen
     */
    public void paint (Graphics2D pen) {
        for (AbstractSpring s : mySprings) {
            s.paint(pen);
        }
        for (Mass m : myMasses) {
            m.paint(pen);
        }
    }

    /**
     * Called by each step of timer, multiple times per second.
     * <p/>
     * This should update the state of the animated shapes by just a little so
     * they appear to move over time.
     */
    public void update (Simulation sim, double dt) {
        updateMovers(sim, dt);
    }

    /**
     * Update all of our Springs and Masses
     * 
     * @param sim
     * @param dt time elapsed since last subsequent update
     */
    void updateMovers (final Simulation sim, double dt) {
        for (AbstractSpring s : mySprings) {
            s.update(dt);
        }
        for (Mass m : myMasses) {
            if (sim.centerOfMassEnabled()) {
                m.applyForce(centerOfMassForce(m,
                        sim.getMyCenterOfMassMagnitude(),
                        sim.getMyCenterOfMassExpVal()));
            }
            m.update(sim, dt);
        }
    }

    /**
     * Returns a Mass based on its id
     * 
     * @param id id of the mass
     * @return the Mass specified by id
     */
    public Mass getMass (int id) {
        for (Mass m : myMasses) {
            if (m.match(id)) return m;
        }
        return null;
    }

    public List<Mass> getMyMasses () {
        return myMasses;
    }

    /**
     * removes the mass and its corresponding spring
     * 
     * @param id
     */
    public void removeMass (int id) {
        Mass toRemove = this.getMass(id);
        myMasses.remove(toRemove);
        Iterator<AbstractSpring> springIterator = mySprings.listIterator();
        while (springIterator.hasNext()) {
            AbstractSpring mySpring = springIterator.next();
            if (mySpring.getMyStart() == toRemove) {
                springIterator.remove();
            }
        }
    }

    public Point2D centerOfMass () {
        double sumOfMasses = 0;
        double xPos = 0;
        double yPos = 0;
        for (Mass m : myMasses) {
            sumOfMasses += m.getMyMass();
            xPos += m.getCenter().getX() * m.getMyMass();
            yPos += m.getCenter().getY() * m.getMyMass();
        }
        return new Point2D.Double(1 / sumOfMasses * xPos, 1 / sumOfMasses
                * yPos);
    }

    public Force centerOfMassForce (Mass m, double myCenterOfMassMagnitude,
            double myCenterOfMassExpVal) {
        Force f = new Force();
        double distance = Force.distanceBetween(m.getCenter(), centerOfMass());
        f.setMagnitude(myCenterOfMassMagnitude
                * Math.pow(distance, -myCenterOfMassExpVal));
        f.setDirection(Force.angleBetween(m.getCenter(), centerOfMass()));
        return f;
    }
}
